package kr.co.cherryt.common;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.ref.WeakReference;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Date;
import java.util.HashMap;
import java.util.Stack;
import java.util.concurrent.ConcurrentHashMap;

import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.os.Environment;
import android.widget.ImageView;

public class ImageCacheManager {

    private static HashMap<String, Bitmap> cacheBitmap=new HashMap<String, Bitmap>();
    private static File cacheDir;

	
	
	private static ImageCacheManager _instance = null;
	
	private static final int IMAGE_CACHE_SIZE = 50;
	private static long CACHE_TIMEOUT = 86400000;	// 0이면 검사 안함 (디폴트 1일)
	
	
	private static ConcurrentHashMap<String, WeakReference<Bitmap>> ICache;//이미지 데이터
    
	public static String mPath;
	private final String IMAGE_CACH_FOLDER="/temp/";//저장경로
	private BitmapFactory.Options options = null;
	private static Context mContext;
//	private Handler mHandler = null;
	
	/**
	 * @getInstance(context)를 호출해서 사용함
	 */
	private ImageCacheManager(Context context) {
		// TODO Auto-generated constructor stub
		mContext = context;
		ICache = new  ConcurrentHashMap<String, WeakReference<Bitmap>>();
	//	mHandler = new Handler();
		getPath();

		
        if (options == null)
        {
        	options = new BitmapFactory.Options();
        	options.inDither = true;
        	options.inPurgeable = true;
        }
        
        //Make the background thead low priority. This way it will not affect the UI performance
    	
    	// PhotosLoader의 전역변수에 쓰레드 우선순위를 설정 (UI 퍼포먼스를 위해서..)
        photoLoaderThread.setPriority(Thread.NORM_PRIORITY-1);
        
        //Find the dir to save cached images
        if (android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED)){
        	String szExtenalFolder = Utility.getBaseExternalFolder(mContext);
        	cacheDir=new File(szExtenalFolder);
        	//cacheDir=new File(android.os.Environment.getExternalStorageDirectory(),"LazyList");
        	
        }
        else{
            cacheDir=context.getCacheDir();
        }
        
        if(!cacheDir.exists())
            cacheDir.mkdirs();
        
	}
	
	
	public static String getDownloadCacheFilePath(Context context, String szUrl){
		//String szRet = String.format("%s%s%d", Utility.getBaseExternalFolder(context), File.separator, szUrl.hashCode());
		String szRet = String.format("%s%s%d",cacheDir.toString(), File.separator, szUrl.hashCode());
		
		return szRet;
	}

	public static boolean isExistsCacheFile(String szUrl){
		
		String szFileName = String.valueOf(szUrl.hashCode());
		// 확인
        File file=new File(cacheDir, szFileName);
        return file.exists();

	}
	
	private static void makeTempDir()
	{
		File dir = new File(mPath);//저장되는 경로 지정
		//image 저장 폴더 생성
		if(!dir.exists()){
			dir.mkdirs();
		}
	}
	
	public synchronized static ImageCacheManager getInstance(Context context) 
	{
		
	    if (_instance == null || ICache == null || mPath == null) 
	    {
	        _instance = new ImageCacheManager(context);

		    //CACHE_TIMEOUT = CherrytData.getProfileExpireTime(context);
		    makeTempDir();
	    }
	    
        
	    return _instance;
	}

	private boolean containsKey(String hashcode)
	{
		return ICache.containsKey(hashcode);
	}
	
	public void put(String szFileName, Bitmap b) {

	}
	
	private static Bitmap get(String hashCode)
	{
		Bitmap d = null;
        WeakReference<Bitmap> ref = ICache.get(hashCode);
        if ( ref != null ) {
            d = ref.get();
            if (d == null)
            	ICache.remove(hashCode);
        }     
        return d;
		
	}
	
	private String getPath(){
		String ext = Environment.getExternalStorageState();
	    String mPath = null;
		if (ext.equals(Environment.MEDIA_MOUNTED))//외,내장 메모리 구분
	    {
			mPath = Environment.getExternalStorageDirectory().getAbsolutePath();
	    }
	    else
	    {
	    	mPath = Environment.getDataDirectory().getAbsolutePath();
	    }
		
		if(mPath.charAt(0)=='/'){
			mPath = mPath.substring(1,mPath.length());
		}
		return this.mPath = mPath + IMAGE_CACH_FOLDER;
	}


	
	private boolean removeCache(String szUrl){
		
        String filename = String.valueOf(szUrl.hashCode());

        new File(cacheDir + File.separator +  filename).delete();        

        cacheBitmap.remove(szUrl);

        CC_Log.info("remove cache", "del cache:" + szUrl);
		CC_Log.info("remove cache", "filename:" + filename);
        
		// 확인
        File f1=new File(cacheDir, filename);
		if(f1.exists()){
			if(f1.delete()){
				CC_Log.info("remove cache: ok!!");
			}else{
				CC_Log.info("remove cache: del err!!");
			}
		}else{
			CC_Log.info("remove cache: not exists!!");
		}
		
		if(cacheBitmap.containsKey(szUrl)){
			CC_Log.info("remove cache: has cache image!!");
		}else{
			CC_Log.info("remove cache: ");
		}
		
		
        return true;
	}
	
	private static boolean checkExpireDate(String szUrl){
		
        String filename=String.valueOf(szUrl.hashCode());
        File f=new File(cacheDir, filename);
        
		if(f.exists())
		{
			if(CACHE_TIMEOUT != 0L)
			{
				//유효기간이 지나면 캐쉬에서 삭제
				long nRemain = (f.lastModified() + CACHE_TIMEOUT) - System.currentTimeMillis();
				
				if(nRemain < 0)
				{
					return true;
				}
				else{
					return false;
				}
			}
		}        
		return false;
	}
	
	/**
	 * 이미지를 다운로드 받아서 ImageView에 넣어준다.
	 * @param szUrl 이미지를 다운로드 받을 주소
	 * @param imgview 다운로드 받은 이미지를 넣을 view
	 */
	public void attachThumbailImage(final String szUrl,final ImageView imgview) { //이미지를 받아오는 스레드
		
		String szId = szUrl;
		
    	if(cacheBitmap.containsKey(szId)){
        	// 이미 다운받은 이미지가 있는 경우 그냥 출력하자!!
        	if(imgview != null){
        		Bitmap bm = cacheBitmap.get(szId);
        		imgview.setImageBitmap(bm);
        	}
        }
        else {
        	// setTag에 url을 넣어야 다운로드 시점에서 이미지 뷰가 갱신된다.
        	imgview.setTag(szUrl);

        	// 다운로드를 시작하고
            queuePhoto(szId, szUrl, imgview);

    		
            // 기본이미지로 설정
            if(imgview != null){
            }
        }
	}

    // 다운로드 큐에 추가한다.
    private void queuePhoto(String szId, String url, ImageView imageView)
    {
    	if(imageView != null){
    		photosQueue.Clean(imageView);
    	}
    	
    	PhotoToLoad p=new PhotoToLoad(szId, url, imageView);
        
        //  photosQueue.photosToLoad에 동시접근을 막기 위해 임계영역 설정
        synchronized(photosQueue.photosToLoad){
            photosQueue.photosToLoad.push(p);
            
            // 모든 
            // http://codemuri.tistory.com/447
            photosQueue.photosToLoad.notifyAll();
        }

        
        // 쓰레드가 아직 시작되지 않았다면 시작!!
        //start thread if it's not started yet
        // Thread.Sate.NEW  (The thread has been created, but has never been started.)
        if(photoLoaderThread.getState()==Thread.State.NEW)
            photoLoaderThread.start();
    }
    
    //stores list of photos to download
    class PhotosQueue {
        private Stack<PhotoToLoad> photosToLoad=new Stack<PhotoToLoad>();
        
        //removes all instances of this ImageView
        public void Clean(ImageView image)
        {
            for(int j=0 ;j<photosToLoad.size();){
                if(photosToLoad.get(j).imageView==image) {
                    photosToLoad.remove(j);
                } else {
                	++j;
                }
            }
        }
    }
    
    
    //Task for the queue
    private class PhotoToLoad {
    	String szUniqueId;
        public String url;
        public ImageView imageView;
        public PhotoToLoad(String szId, String u, ImageView i) {
        	szUniqueId = szId;
            url=u; 
            imageView=i;
        }
    }
    
    
    class PhotosLoader extends Thread {
        public void run() {
            try {
                while(true)
                {
                    //thread waits until there are any images to load in the queue
                    if(photosQueue.photosToLoad.size()==0)
                        synchronized(photosQueue.photosToLoad){
                            photosQueue.photosToLoad.wait();
                        }
                    if(photosQueue.photosToLoad.size()!=0)
                    {
                        PhotoToLoad photoToLoad;
                        synchronized(photosQueue.photosToLoad){
                            photoToLoad=photosQueue.photosToLoad.pop();
                        }
                        
                        // url null check
                        if(!Utility.IsEmpty(photoToLoad.url)){
                            Bitmap bmp=getBitmap(photoToLoad.url);
                            	
                            // null check
                            if(bmp != null)
                            {
                                cacheBitmap.put(photoToLoad.szUniqueId, bmp);
                                
                                if(photoToLoad.imageView != null){
                                    Object tag=photoToLoad.imageView.getTag();
                                    if(tag!=null && ((String)tag).equals(photoToLoad.url)){
                                        BitmapDisplayer bd=new BitmapDisplayer(bmp, photoToLoad.imageView);
                                        Activity a=(Activity)photoToLoad.imageView.getContext();
                                        a.runOnUiThread(bd);
                                    }
                                }
                            }
                        }
                    }
                    if(Thread.interrupted())
                        break;
                }
            } catch (InterruptedException e) {
                //allow thread to exit
            }
        }
    }	    
    
    PhotosQueue photosQueue=new PhotosQueue();
    PhotosLoader photoLoaderThread=new PhotosLoader();

    
    private Bitmap getBitmap(String url) 
    {
        //I identify images by hashcode. Not a perfect solution, good for the demo.
        String filename=String.valueOf(url.hashCode());
        File f=new File(cacheDir, filename);
        
        //from SD cache
        Bitmap b = decodeFile(f);
        if(b!=null){
            return b;
        }
        
        //from web
        try {
            Bitmap bitmap=null;
            InputStream is=new URL(url).openStream();
            OutputStream os = new FileOutputStream(f);
            Utils.CopyStream(is, os);
            //Utils.DownloadImageEx(url, android.os.Environment.getExternalStorageDirectory()+ File.separator + "LazyList", filename);
            os.close();
            is.close();
            bitmap = decodeFile(f);
            return bitmap;
        } catch (Exception ex){
           ex.printStackTrace();
           return null;
        }
    }
    
    
    
    //decodes image and scales it to reduce memory consumption
    private Bitmap decodeFile(File file){
        try {
        	
            return BitmapFactory.decodeStream(new FileInputStream(file),null,null);

            // 원 소스는 아래 70 * 70으로 리사이징 했으나 큰 이미지가 사용되는  경우 해상도가 떨어지는 부분이 있어 주석처리.... enes
            // 향후 사용 될 수 있으므로 주석 제거하지 말자
            //decode image size
/*	        	
	            BitmapFactory.Options o = new BitmapFactory.Options();
	            o.inJustDecodeBounds = true;
	            return BitmapFactory.decodeStream(new FileInputStream(file),null,o);
	            
	            //Find the correct scale value. It should be the power of 2.
	            final int REQUIRED_SIZE=70;
	            int width_tmp=o.outWidth, height_tmp=o.outHeight;
	            int scale=1;
	            while(true){
	                if(width_tmp/2<REQUIRED_SIZE || height_tmp/2<REQUIRED_SIZE)
	                    break;
	                width_tmp/=2;
	                height_tmp/=2;
	                scale*=2;
	            }
	            
	            //decode with inSampleSize
	            BitmapFactory.Options o2 = new BitmapFactory.Options();
	            o2.inSampleSize=scale;
	            return BitmapFactory.decodeStream(new FileInputStream(file), null, o2);
*/	            
        } catch (FileNotFoundException e) {}
        return null;
    }
    
    
    
    
    
    
    //Used to display bitmap in the UI thread
    class BitmapDisplayer implements Runnable
    {
        Bitmap bitmap;
        ImageView imageView;
        public BitmapDisplayer(Bitmap b, ImageView i){bitmap=b;imageView=i;}
        public void run()
        {
            if(bitmap!=null){
            	
            	// resizing을 위한 비트맵
                setThumbnailImage(imageView, bitmap);
            	return;
            }

        }
    }

    private void setThumbnailImage(ImageView imgView, Bitmap bm){
    	
    	if(bm != null){    	
			imgView.setImageBitmap(bm);
    	}
    }
	    
	    
	    
	    
	    
	    
	/**
	 * @param szUrl 이미지를 다운로드 받을 주소
	 * @param imgview 다운로드 받은 이미지를 넣을 view
	 * @param img 기본이미지로 설정할 Drawable
	 */
	public void attachThumbailImage(final String szUrl,final ImageView imgview, final Drawable img)//이미지를 받아오는 스레드
	{
		imgview.setImageDrawable(img);
		attachThumbailImage(szUrl, imgview);
/*
		mHandler.post(new Runnable() {
			@Override
			public void run() {
				imgview.setImageDrawable(img);
				imgview.setTag(new SetDrawableWorker(szUrl,imgview));
			}
		});	
*/			

	}
    private class SetDrawableWorker {
    	
        private final WeakReference<ImageDownloader> bitmapDownloaderTaskReference;

        public SetDrawableWorker(String szUrl, ImageView imgview) {
            super();
            ImageDownloader task = new ImageDownloader(szUrl,imgview,this);
            bitmapDownloaderTaskReference = new WeakReference<ImageDownloader>(task);
            task.execute();
        }
        public ImageDownloader getBitmapDownloaderTask() {
            return bitmapDownloaderTaskReference.get();
        }
 
  
    }
    private boolean cancelPotentialDownload(String url, ImageView imageView) {
    	ImageDownloader imageDownloader = getBitmapDownloaderTask(imageView);

        if (imageDownloader != null) {
            String bitmapUrl = imageDownloader.url;
            if ((bitmapUrl == null) || (!bitmapUrl.equals(url))) {
            	imageDownloader.cancel(true);
            } else {
                // The same URL is already being downloaded.
                return false;
            }
        }
        return true;
    }
    
	private class ImageDownloader extends AsyncTask<String, Void, Bitmap> { 

		private String url;
		private final WeakReference<ImageView> iv;
		private SetDrawableWorker sdw;

		public ImageDownloader(String szUrl, ImageView imageView, SetDrawableWorker setDrawableWorker) {
			this.iv = new WeakReference<ImageView>(imageView);
			this.url = szUrl;
			this.sdw = setDrawableWorker;
		}
		
		@Override 
		protected Bitmap doInBackground(String... params) {
			Bitmap result = null;
			if (iv != null && url != null && url.trim() != "" && !isCancelled()) 
			{
				String szFileName = String.valueOf(url.hashCode());
				result = ImageCacheManager.get(szFileName);
					
					if(result != null)
					{						
						if(cancelPotentialDownload(url, iv.get()))
						{
//							Log.e("####","other url " + url.substring(url.lastIndexOf("/")));
							iv.get().setTag(sdw);
						}
						else
						{
//							Log.e("####","sam url " + url.substring(url.lastIndexOf("/")));
							return null;
						}
					}
					else
					{
						iv.get().setTag(sdw);
						result = Imageget(url,true);
					}
			}
			
			return result;
		}

		@Override
		protected void onPostExecute(Bitmap result) {
			if (iv != null && result != null && !isCancelled()) {
                ImageView imageView = iv.get();
                ImageDownloader bitmapDownloaderTask = getBitmapDownloaderTask(imageView);
                if ((this == bitmapDownloaderTask)) {
                	imageView.setImageBitmap(result);
                }
            }
			
		}

		@Override
		protected void onPreExecute() {
			super.onPreExecute();
		}

	}
    private ImageDownloader getBitmapDownloaderTask(ImageView imageView) {
        if (imageView != null)
        {
        	SetDrawableWorker worker = (SetDrawableWorker) imageView.getTag();
            if (worker != null) 
            {
                return worker.getBitmapDownloaderTask();
            }
        }
        return null;
    }
    
	private Bitmap Imageget(String szUrl, boolean reTry){//생성자 값으로 주소값을 넣는다.
		
			String szFileName = String.valueOf(szUrl.hashCode());
			Bitmap imagetrue = null;
			
			if(containsKey(szFileName)){
				imagetrue = get(szFileName);
			}
			else
			{
				boolean timeout = false;
				
				File f = new File(mPath+szFileName);
				if(f.exists())
				{
					if(CACHE_TIMEOUT != 0)
					{
						timeout = f.lastModified() + CACHE_TIMEOUT < new Date().getTime();
						//오래된 파일은 다시 받는다.
					}
		            
					if(!timeout)
					{	
			            
						imagetrue = BitmapFactory.decodeFile(mPath + szFileName,options);	
						if(imagetrue != null)
						{
							put(szFileName, imagetrue);
						}
					}
				}
			}
			
			if(imagetrue == null)
			{
				if(DownloadImage(szUrl,szFileName))
				{
					imagetrue = BitmapFactory.decodeFile(mPath + szFileName ,options);
					put(szFileName,imagetrue);
				}
				else
				{
					if(reTry)
					{
						//실패 하면 한번만 다시 받는다.
						return Imageget(szUrl,false);
					}
					else
					{
						return null;
					}
				}

			}
			return imagetrue;
	}

	private boolean DownloadImage(String szKey,String szFileName){//이미지 파일 저장
		URL szUrl;
		int Read;
		HttpURLConnection conn = null;
		InputStream is = null;
		FileOutputStream fos = null;
		
		try {
			szUrl = new URL( szKey);
			conn = (HttpURLConnection)szUrl.openConnection();
			int len = conn.getContentLength();
			byte[] raster = new byte[len];
			is = conn.getInputStream();

			fos = new FileOutputStream(mPath + szFileName);

			for(;;){
				Read = is.read(raster);
				if(Read <= 0)
					break;
				fos.write(raster,0,Read);
			}
			
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		} finally {
			try {
				if(is != null)
					is.close();
				if(fos != null)
					fos.close();
			} catch (IOException e) {
			}
			if(conn != null)
				conn.disconnect();
		}

		return true;
	}
}